#include "useragent.h"

#ifdef Q_OS_WIN32
#include <windows.h>

enum WindowsVersion {
    // CE-based versions
    WindowsCE1 = 0,
    WindowsCE2,
    WindowsCE3,
    WindowsCE4,
    WindowsCE5,
    WindowsCE6,
    WindowsCE7,
    // 3.x-based versions
    Windows3_1,
    // 9x-based versions
    Windows95,
    Windows98,
    WindowsME,
    // NT-based versions
    WindowsNT3,
    WindowsNT4,
    Windows2000,
    WindowsXP,
    WindowsServer2003,
    WindowsVista,
    WindowsServer2008,
    Windows7
};

WindowsVersion windowsVersion(int* major, int* minor)
{
    static bool initialized = false;
    static WindowsVersion version;
    static int majorVersion, minorVersion;

    if (!initialized) {
        initialized = true;
#if WINCE
        OSVERSIONINFO versionInfo;
#else
        OSVERSIONINFOEX versionInfo;
#endif
        ZeroMemory(&versionInfo, sizeof(versionInfo));
        versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
        GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&versionInfo));
        majorVersion = versionInfo.dwMajorVersion;
        minorVersion = versionInfo.dwMinorVersion;

#if WINCE
        if (majorVersion >= 1 && majorVersion <= 7)
            version = static_cast<WindowsVersion>(WindowsCE1 + (majorVersion - 1));
        else
            version = (majorVersion < 1) ? WindowsCE1 : WindowsCE7;
#else
        if (versionInfo.dwPlatformId == VER_PLATFORM_WIN32s)
            version = Windows3_1;
        else if (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
            if (!minorVersion)
                version = Windows95;
            else
                version = (minorVersion == 10) ? Windows98 : WindowsME;
        } else {
            if (majorVersion == 5) {
                if (!minorVersion)
                    version = Windows2000;
                else
                    version = (minorVersion == 1) ? WindowsXP : WindowsServer2003;
            } else if (majorVersion >= 6) {
                if (versionInfo.wProductType == VER_NT_WORKSTATION)
                    version = (majorVersion == 6 && !minorVersion) ? WindowsVista : Windows7;
                else
                    version = WindowsServer2008;
            } else
                version = (majorVersion == 4) ? WindowsNT4 : WindowsNT3;
        }
#endif
    }

    if (major)
        *major = majorVersion;
    if (minor)
        *minor = minorVersion;
    return version;
}

static QString osVersionForUAString()
{
    int major, minor;
    WindowsVersion version = windowsVersion(&major, &minor);
    switch (version) {
    case WindowsCE1:
    case WindowsCE2:
    case WindowsCE3:
        return "Windows CE";
    case WindowsCE4:
        return "Windows CE .NET";
    case Windows3_1:
        return "Windows 3.1";
    case Windows95:
        return "Windows 95";
    case Windows98:
        return "Windows 98";
    case WindowsME:
        return "Windows 98; Win 9x 4.90";
    case WindowsNT4:
        return "WinNT4.0";
    }

    const char* familyName = (version >= WindowsNT3) ? "Windows NT " : "Windows CE ";
    return QString(familyName + QString::number(major) + '.' + QString::number(minor));
}

#if !WINCE
static bool isWOW64()
{
    static bool initialized = false;
    static bool wow64 = false;

    if (!initialized) {
        initialized = true;
        HMODULE kernel32Module = GetModuleHandleA("kernel32.dll");
        if (!kernel32Module)
            return wow64;
        typedef BOOL (WINAPI* IsWow64ProcessFunc)(HANDLE, PBOOL);
        IsWow64ProcessFunc isWOW64Process = reinterpret_cast<IsWow64ProcessFunc>(GetProcAddress(kernel32Module, "IsWow64Process"));
        if (isWOW64Process) {
            BOOL result = FALSE;
            wow64 = isWOW64Process(GetCurrentProcess(), &result) && result;
        }
    }

    return wow64;
}

static WORD processorArchitecture()
{
    static bool initialized = false;
    static WORD architecture = PROCESSOR_ARCHITECTURE_INTEL;

    if (!initialized) {
        initialized = true;
        HMODULE kernel32Module = GetModuleHandleA("kernel32.dll");
        if (!kernel32Module)
            return architecture;
        typedef VOID (WINAPI* GetNativeSystemInfoFunc)(LPSYSTEM_INFO);
        GetNativeSystemInfoFunc getNativeSystemInfo = reinterpret_cast<GetNativeSystemInfoFunc>(GetProcAddress(kernel32Module, "GetNativeSystemInfo"));
        if (getNativeSystemInfo) {
            SYSTEM_INFO systemInfo;
            ZeroMemory(&systemInfo, sizeof(systemInfo));
            getNativeSystemInfo(&systemInfo);
            architecture = systemInfo.wProcessorArchitecture;
        }
    }

    return architecture;
}
#endif

static QString architectureTokenForUAString()
{
#if !WINCE
    if (isWOW64())
        return "; WOW64";
    if (processorArchitecture() == PROCESSOR_ARCHITECTURE_AMD64)
        return "; Win64; x64";
    if (processorArchitecture() == PROCESSOR_ARCHITECTURE_IA64)
        return "; Win64; IA64";
#endif
    return QString();
}

QString windowsVersionForUAString()
{
    return QString(osVersionForUAString() + architectureTokenForUAString());
}
#endif

QString createuseragent()
{
    static QString firstPart;
    static QString secondPart;
    static QString thirdPart;

    if (firstPart.isNull() || secondPart.isNull() || thirdPart.isNull()) {
        QString firstPartTemp;
        firstPartTemp.reserve(150);
        firstPartTemp += QString::fromLatin1("Mozilla/5.0 ("

        // Platform
    #ifdef Q_WS_MAC
            "Macintosh; "
    #elif defined Q_WS_QWS
            "QtEmbedded; "
    #elif defined Q_WS_MAEMO_5
            "Maemo"
    #elif defined Q_WS_MAEMO_6
            "MeeGo"
    #elif defined Q_WS_WIN
            // Nothing
    #elif defined Q_WS_X11
            "X11; "
    #elif defined Q_OS_SYMBIAN
            "Symbian"
    #else
            "Unknown; "
    #endif
        );

    #if defined Q_OS_SYMBIAN
            QSysInfo::SymbianVersion symbianVersion = QSysInfo::symbianVersion();
            switch (symbianVersion) {
            case QSysInfo::SV_9_2:
                firstPartTemp += QString::fromLatin1("OS/9.2; ");
                break;
            case QSysInfo::SV_9_3:
                firstPartTemp += QString::fromLatin1("OS/9.3; ");
                break;
            case QSysInfo::SV_9_4:
                firstPartTemp += QString::fromLatin1("OS/9.4; ");
                break;
            case QSysInfo::SV_SF_2:
                firstPartTemp += QString::fromLatin1("/2; ");
                break;
            case QSysInfo::SV_SF_3:
                firstPartTemp += QString::fromLatin1("/3; ");
                break;
            case QSysInfo::SV_SF_4:
                firstPartTemp += QString::fromLatin1("/4; ");
                break;
            default:
                firstPartTemp += QString::fromLatin1("; ");
                break;
            }
    #endif

    #if defined(QT_NO_OPENSSL)
            // No SSL support
            firstPartTemp += QString::fromLatin1("N; ");
    #endif

            // Operating system
    #ifdef Q_OS_AIX
            firstPartTemp += QString::fromLatin1("AIX");
    #elif defined Q_OS_WIN32
            firstPartTemp += windowsVersionForUAString();
    #elif defined Q_OS_DARWIN
    #ifdef __i386__ || __x86_64__
            firstPartTemp += QString::fromLatin1("Intel Mac OS X");
    #else
            firstPartTemp += QString::fromLatin1("PPC Mac OS X");
    #endif

    #elif defined Q_OS_BSDI
            firstPartTemp += QString::fromLatin1("BSD");
    #elif defined Q_OS_BSD4
            firstPartTemp += QString::fromLatin1("BSD Four");
    #elif defined Q_OS_CYGWIN
            firstPartTemp += QString::fromLatin1("Cygwin");
    #elif defined Q_OS_DGUX
            firstPartTemp += QString::fromLatin1("DG/UX");
    #elif defined Q_OS_DYNIX
            firstPartTemp += QString::fromLatin1("DYNIX/ptx");
    #elif defined Q_OS_FREEBSD
            firstPartTemp += QString::fromLatin1("FreeBSD");
    #elif defined Q_OS_HPUX
            firstPartTemp += QString::fromLatin1("HP-UX");
    #elif defined Q_OS_HURD
            firstPartTemp += QString::fromLatin1("GNU Hurd");
    #elif defined Q_OS_IRIX
            firstPartTemp += QString::fromLatin1("SGI Irix");
    #elif defined Q_OS_LINUX
    #if !defined(Q_WS_MAEMO_5) && !defined(Q_WS_MAEMO_6)

    #if defined(__x86_64__)
            firstPartTemp += QString::fromLatin1("Linux x86_64");
    #elif defined(__i386__)
            firstPartTemp += QString::fromLatin1("Linux i686");
    #else
            firstPartTemp += QString::fromLatin1("Linux");
    #endif
    #endif

    #elif defined Q_OS_LYNX
            firstPartTemp += QString::fromLatin1("LynxOS");
    #elif defined Q_OS_NETBSD
            firstPartTemp += QString::fromLatin1("NetBSD");
    #elif defined Q_OS_OS2
            firstPartTemp += QString::fromLatin1("OS/2");
    #elif defined Q_OS_OPENBSD
            firstPartTemp += QString::fromLatin1("OpenBSD");
    #elif defined Q_OS_OS2EMX
            firstPartTemp += QString::fromLatin1("OS/2");
    #elif defined Q_OS_OSF
            firstPartTemp += QString::fromLatin1("HP Tru64 UNIX");
    #elif defined Q_OS_QNX6
            firstPartTemp += QString::fromLatin1("QNX RTP Six");
    #elif defined Q_OS_QNX
            firstPartTemp += QString::fromLatin1("QNX");
    #elif defined Q_OS_RELIANT
            firstPartTemp += QString::fromLatin1("Reliant UNIX");
    #elif defined Q_OS_SCO
            firstPartTemp += QString::fromLatin1("SCO OpenServer");
    #elif defined Q_OS_SOLARIS
            firstPartTemp += QString::fromLatin1("Sun Solaris");
    #elif defined Q_OS_ULTRIX
            firstPartTemp += QString::fromLatin1("DEC Ultrix");
    #elif defined Q_OS_SYMBIAN
            firstPartTemp += QLatin1Char(' ');
            QSysInfo::S60Version s60Version = QSysInfo::s60Version();
            switch (s60Version) {
            case QSysInfo::SV_S60_3_1:
                firstPartTemp += QString::fromLatin1("Series60/3.1");
                break;
            case QSysInfo::SV_S60_3_2:
                firstPartTemp += QString::fromLatin1("Series60/3.2");
                break;
            case QSysInfo::SV_S60_5_0:
                firstPartTemp += QString::fromLatin1("Series60/5.0");
                break;
            default:
                break;
            }
    #elif defined Q_OS_UNIX
            firstPartTemp += QString::fromLatin1("UNIX BSD/SYSV system");
    #elif defined Q_OS_UNIXWARE
            firstPartTemp += QString::fromLatin1("UnixWare Seven, Open UNIX Eight");
    #else
            firstPartTemp += QString::fromLatin1("Unknown");
    #endif

    firstPartTemp.squeeze();
    firstPart = firstPartTemp;

    QString secondPartTemp;
    secondPartTemp.reserve(150);
    secondPartTemp += QString::fromLatin1(") ");

    // webkit/qt version
    secondPartTemp += QString::fromLatin1("AppleWebKit/");
    secondPartTemp += qWebKitVersion();
    secondPartTemp += QString::fromLatin1(" (KHTML, like Gecko) ");


    // Application name split the third part
    secondPartTemp.squeeze();
    secondPart = secondPartTemp;

    QString thirdPartTemp;
    thirdPartTemp.reserve(150);
    #if defined(Q_OS_SYMBIAN) || defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6)
            thirdPartTemp += QLatin1String(" Mobile Safari/");
    #else
            thirdPartTemp += QLatin1String(" Safari/");
    #endif
        thirdPartTemp += qWebKitVersion();
        thirdPartTemp.squeeze();
        thirdPart = thirdPartTemp;
        Q_ASSERT(!firstPart.isNull());
        Q_ASSERT(!secondPart.isNull());
        Q_ASSERT(!thirdPart.isNull());
    }

    // Application name/version
    QString appName = "mySpaces";
    QString appVer = APP_VERSION;
    appName.append(QLatin1Char('/') + appVer);
    // Qt version
    QString Qt = QString::fromLatin1(" Qt/") + QString::fromLatin1(qVersion());

    return firstPart + secondPart + appName + Qt + thirdPart;
}
